
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>

#include "echo.h"
#include "speed_test.h"

#define MAX_MARKER_LEN          50
#define SUBMITTER_INFO_LEN      128

#define ALGORITHM_NAME          "echo"
/////////////////////////////////////////////////////////////////////////////////////
#define SALT_A 0x00000000
#define SALT_B 0x00000000
#define SALT_C 0x00000000
#define SALT_D 0x00000000

#define  USINT     0
#define  UINT      1
#define  ULL       2

#define ET_BIG_ENDIAN 		0
#define ET_LITTLE_ENDIAN	1
#define ET_MIDDLE_ENDIAN	2

unsigned long long CNT_r;
unsigned int r;
unsigned char endian;

/***************************Endianess routines***********************************/
unsigned char Endianess(){
/*	Endianess test								*/
	unsigned int T;
	unsigned char T_o[8];

	T = 0xAABBCCDD;
	*(unsigned int*)T_o = T;
	if(T_o[0] == 0xAA){
		return ET_BIG_ENDIAN;
	}
	if(T_o[0] == 0xDD){
		return ET_LITTLE_ENDIAN;
	}
	else{
		return ET_MIDDLE_ENDIAN;
	}
}

void PushString(unsigned long long num, unsigned char* string, unsigned char type){
	unsigned int j;

	if(type == USINT){
		for(j=0; j<2; j++){
	  		string[j] = (num >> (j*8)) & 0xFF;
		}
	}
	if(type == UINT){
		for(j=0; j<4; j++){
	  		string[j] = (num >> (j*8)) & 0xFF;
		}
	}
	if(type == ULL){
		for(j=0; j<8; j++){
	  		string[j] = (num >> (j*8)) & 0xFF;
		}
	}

	return;
}

unsigned char flip_bits(unsigned char c){
	unsigned char res;
	unsigned char bits[8];
	unsigned int i;

	for(i=0; i<8; i++)
		bits[7-i] = (c >> i) & 0x1;

	res = 0;
	for(i=0; i<8; i++)
		res ^= bits[i] << i;

	return res;
}

#ifdef SALT_OPTION
/***************************Changing SALT routine  ***********************************/
HashReturn SetSalt(hashState* state, const BitSequence* SALT){
	unsigned int topush = *((unsigned int*)SALT);
	PushString(topush, state->SALT, UINT);
	topush = *((unsigned int*)(SALT+4));
	PushString(topush, state->SALT+4, UINT);
	topush = *((unsigned int*)(SALT+8));
	PushString(topush, state->SALT+8, UINT);
	topush = *((unsigned int*)(SALT+12));
	PushString(topush, state->SALT+12, UINT);

	return SUCCESS;
}
#endif

/***************************Initialisation routines***********************************/
HashReturn Init(hashState *state, unsigned int hashbitlen)
{
	unsigned int i, j;
	unsigned char endianess;

	memset(state->state, 0, 16*16);
  	/* set some initial values	    */
  	if(hashbitlen <= 256)
	 	state->end_cv = 16;
  	else
	  	state->end_cv = 2*16;

  	/* load the IV in the internal state */
 	for(i=0; i< state->end_cv; i++){
		if(i%4 == 0){
			PushString(hashbitlen, (unsigned char*)(state->state+i), UINT);
		}
		else{
  			state->state[i] = 0;
		}
	}


	state->hashbitlen = hashbitlen;
	state->messbitlen = 0;
	if(hashbitlen <= 256)
		state->m_blocksize = 1536;
	else
		state->m_blocksize = 1024;

    	/* set the counter */
  	state->CNT = 0;
	state->padding = 0;

    	/* set the number of rounds */
  	if(hashbitlen <= 256)
		state->r = 8;
	else
		state->r = 10;

#ifdef SALT_OPTION
  	/* set the default salt */
  	memset(state->SALT, 0, 16);
#endif

	/*	Endianess testing	   */
	endian = Endianess();

	return SUCCESS;
}


/***************************Basic compression routines***********************************/
#define abyte(k)  ((k)        & 0xff)
#define bbyte(k) (((k) >>  8) & 0xff)
#define cbyte(k) (((k) >> 16) & 0xff)
#define dbyte(k)  ((k) >> 24)

#define integer(s, i) (*((unsigned int*)(s) + i))

#ifdef SALT_OPTION
#define AESround1(s, a, b, c, d) do { \
  a = T0[ abyte(integer(s, 0)) ] ^ T1[ bbyte(integer(s, 1)) ] ^ T2[ cbyte(integer(s, 2)) ] ^ T3[ dbyte(integer(s, 3)) ] ^ CNT_r; \
  b = T0[ abyte(integer(s, 1)) ] ^ T1[ bbyte(integer(s, 2)) ] ^ T2[ cbyte(integer(s, 3)) ] ^ T3[ dbyte(integer(s, 0)) ] ^ (CNT_r >> 32); \
  c = T0[ abyte(integer(s, 2)) ] ^ T1[ bbyte(integer(s, 3)) ] ^ T2[ cbyte(integer(s, 0)) ] ^ T3[ dbyte(integer(s, 1)) ]; \
  d = T0[ abyte(integer(s, 3)) ] ^ T1[ bbyte(integer(s, 0)) ] ^ T2[ cbyte(integer(s, 1)) ] ^ T3[ dbyte(integer(s, 2)) ]; \
} while(0);
#define AESround2(s, a, b, c, d) do { \
  integer(s, 0) = T0[ abyte(a) ] ^ T1[ bbyte(b) ] ^ T2[ cbyte(c) ] ^ T3[ dbyte(d) ] ^ integer(state->SALT, 0); \
  integer(s, 1) = T0[ abyte(b) ] ^ T1[ bbyte(c) ] ^ T2[ cbyte(d) ] ^ T3[ dbyte(a) ] ^ integer(state->SALT, 1); \
  integer(s, 2) = T0[ abyte(c) ] ^ T1[ bbyte(d) ] ^ T2[ cbyte(a) ] ^ T3[ dbyte(b) ] ^ integer(state->SALT, 2); \
  integer(s, 3) = T0[ abyte(d) ] ^ T1[ bbyte(a) ] ^ T2[ cbyte(b) ] ^ T3[ dbyte(c) ] ^ integer(state->SALT, 3); \
} while(0);
#else
#define AESround1(s, a, b, c, d) do { \
  a = T0[ abyte(integer(s, 0)) ] ^ T1[ bbyte(integer(s, 1)) ] ^ T2[ cbyte(integer(s, 2)) ] ^ T3[ dbyte(integer(s, 3)) ] ^ CNT_r; \
  b = T0[ abyte(integer(s, 1)) ] ^ T1[ bbyte(integer(s, 2)) ] ^ T2[ cbyte(integer(s, 3)) ] ^ T3[ dbyte(integer(s, 0)) ] ^ (CNT_r >> 32); \
  c = T0[ abyte(integer(s, 2)) ] ^ T1[ bbyte(integer(s, 3)) ] ^ T2[ cbyte(integer(s, 0)) ] ^ T3[ dbyte(integer(s, 1)) ]; \
  d = T0[ abyte(integer(s, 3)) ] ^ T1[ bbyte(integer(s, 0)) ] ^ T2[ cbyte(integer(s, 1)) ] ^ T3[ dbyte(integer(s, 2)) ]; \
} while(0);
#define AESround2(s, a, b, c, d) do { \
  integer(s, 0) = T0[ abyte(a) ] ^ T1[ bbyte(b) ] ^ T2[ cbyte(c) ] ^ T3[ dbyte(d) ] ^ SALT_A; \
  integer(s, 1) = T0[ abyte(b) ] ^ T1[ bbyte(c) ] ^ T2[ cbyte(d) ] ^ T3[ dbyte(a) ] ^ SALT_B; \
  integer(s, 2) = T0[ abyte(c) ] ^ T1[ bbyte(d) ] ^ T2[ cbyte(a) ] ^ T3[ dbyte(b) ] ^ SALT_C; \
  integer(s, 3) = T0[ abyte(d) ] ^ T1[ bbyte(a) ] ^ T2[ cbyte(b) ] ^ T3[ dbyte(c) ] ^ SALT_D; \
} while(0);
#endif

#define WORD_ROUND(s, j, i) do {\
  register unsigned int a, b, c, d;\
  AESround1(s, a, b, c, d);\
  AESround2(s, a, b, c, d);\
  CNT_r++;     \
} while(0);

#define MDS(SA, SB, SC, SD) do { \
  unsigned int a, b, c, d, e, f, g;\
  e   = (integer(SA,0) ^ integer(SB,0));\
  f   = (integer(SC,0) ^ integer(SD,0));\
  g   = (integer(SA,0) ^ integer(SD,0));\
  a   = e ^ f;\
  b   =(((e >> 7)&0x01010101)*27)  ^ ((e << 1)&0xfefefefe);\
  c   =(((f >> 7)&0x01010101)*27)  ^ ((f << 1)&0xfefefefe);\
  d   =(((g >> 7)&0x01010101)*27)  ^ ((g << 1)&0xfefefefe);\
  integer(SC,0)   ^= (a ^ c);					\
  integer(SD,0)   ^= (a ^ d);					\
  integer(SA,0)   ^= (a ^ b);					\
  integer(SB,0)   ^= (a ^ b ^ c ^ d);			\
} while(0);


#define BIG_SUB_WORDS(state) do{\
	WORD_ROUND((state->state)   , 0, 0);\
	WORD_ROUND((state->state+4) , 0, 1);\
	WORD_ROUND((state->state+8) , 0, 2);\
	WORD_ROUND((state->state+12), 0, 3);\
	WORD_ROUND((state->state+16), 1, 0);\
	WORD_ROUND((state->state+20), 1, 1);\
	WORD_ROUND((state->state+24), 1, 2);\
	WORD_ROUND((state->state+28), 1, 3);\
	WORD_ROUND((state->state+32), 2, 0);\
	WORD_ROUND((state->state+36), 2, 1);\
	WORD_ROUND((state->state+40), 2, 2);\
	WORD_ROUND((state->state+44), 2, 3);\
	WORD_ROUND((state->state+48), 3, 0);\
	WORD_ROUND((state->state+52), 3, 1);\
	WORD_ROUND((state->state+56), 3, 2);\
	WORD_ROUND((state->state+60), 3, 3);\
} while(0);

#define BIG_MIX_COLUMN(state) do{\
	MDS(state->state, state->state+4, state->state+8, state->state+12);\
	MDS(state->state+1, state->state+1+4, state->state+1+8, state->state+1+12);\
	MDS(state->state+2, state->state+2+4, state->state+2+8, state->state+2+12);\
	MDS(state->state+3, state->state+3+4, state->state+3+8, state->state+3+12);\
	\
	MDS(state->state+16, state->state+16+4, state->state+16+8, state->state+16+12);\
	MDS(state->state+16+1, state->state+16+1+4, state->state+16+1+8, state->state+16+1+12);\
	MDS(state->state+16+2, state->state+16+2+4, state->state+16+2+8, state->state+16+2+12);\
	MDS(state->state+16+3, state->state+16+3+4, state->state+16+3+8, state->state+16+3+12);\
	\
	MDS(state->state+16*2, state->state+16*2+4, state->state+16*2+8, state->state+16*2+12);\
	MDS(state->state+16*2+1, state->state+16*2+1+4, state->state+16*2+1+8, state->state+16*2+1+12);\
	MDS(state->state+16*2+2, state->state+16*2+2+4, state->state+16*2+2+8, state->state+16*2+2+12);\
	MDS(state->state+16*2+3, state->state+16*2+3+4, state->state+16*2+3+8, state->state+16*2+3+12);\
	\
	MDS(state->state+16*3, state->state+16*3+4, state->state+16*3+8, state->state+16*3+12);\
	MDS(state->state+16*3+1, state->state+16*3+1+4, state->state+16*3+1+8, state->state+16*3+1+12);\
	MDS(state->state+16*3+2, state->state+16*3+2+4, state->state+16*3+2+8, state->state+16*3+2+12);\
	MDS(state->state+16*3+3, state->state+16*3+3+4, state->state+16*3+3+8, state->state+16*3+3+12);\
} while(0);

#define WORD_SWAP(state, a, b) do { \
  unsigned int aa, bb;\
  aa = *(state->state+4*a);   *(state->state+4*a)   = *(state->state+4*b);    *(state->state+4*b)   = aa;\
  bb = *(state->state+4*a+1); *(state->state+4*a+1) = *(state->state+4*b+1);  *(state->state+4*b+1) = bb;\
  aa = *(state->state+4*a+2); *(state->state+4*a+2) = *(state->state+4*b+2);  *(state->state+4*b+2) = aa;\
  bb = *(state->state+4*a+3); *(state->state+4*a+3) = *(state->state+4*b+3);  *(state->state+4*b+3) = bb;\
} while(0);

#define BIG_SHIFT_ROWS(state) do {\
	WORD_SWAP(state, 9,  13);\
	WORD_SWAP(state, 5,  13);\
	WORD_SWAP(state, 1,  13);\
	\
	WORD_SWAP(state, 2,  10);\
	WORD_SWAP(state, 6,  14);\
	\
	WORD_SWAP(state, 3,  15);\
	WORD_SWAP(state, 7,  15);\
	WORD_SWAP(state, 11, 15);\
} while(0);\


#define LOADmessage(state, data) do {\
	if(state->hashbitlen <= 256){\
		memcpy(state->state+16, data, 1536 >> 3);\
	}\
	else{\
		memcpy(state->state+2*16, data, 1024 >> 3);\
	}\
}while(0);

#define XOR_MEM(A, B, size) do { \
  	unsigned int i;\
  	for(i=0; i<size; i++)\
		((unsigned int*)A)[i] ^=  ((unsigned int*)B)[i];\
} while(0);

#define FEEDforward(state, oldcv) do{\
	if(state->end_cv == 16){\
		XOR_MEM(state->state, 			state->state + state->end_cv, 	state->end_cv);\
		XOR_MEM(state->state + 2*state->end_cv, state->state + 3*state->end_cv, state->end_cv);\
		XOR_MEM(state->state, 			state->state + 2*state->end_cv, state->end_cv);\
		XOR_MEM(state->state, 			oldcv, 				state->end_cv);\
	}\
	else{\
		XOR_MEM(state->state, state->state + state->end_cv, state->end_cv);\
		XOR_MEM(state->state, oldcv, state->end_cv);\
	}\
}while(0);

#define SAVEcv(state, oldcv) do {\
	memcpy(oldcv, state, (state->end_cv)*sizeof(int)); \
	if(state->end_cv == 16){\
		XOR_MEM(oldcv, state->state + state->end_cv, 	state->end_cv);\
		XOR_MEM(oldcv, state->state + 2*state->end_cv, 	state->end_cv);\
		XOR_MEM(oldcv, state->state + 3*state->end_cv, 	state->end_cv);\
	}\
	else{\
		XOR_MEM(oldcv, state->state + state->end_cv, 	state->end_cv);\
	}\
} while(0);

#define int_revert(in)  do {\
	unsigned int j;\
	unsigned int temp = 0;\
	for(j=0; j<4; j++)\
		temp  ^= (((in)>>(8*(3-j)))&0xff) << (8*j);\
	in = temp;\
}while(0);

#define reverse_state(state) do {\
	unsigned int i;\
	for(i=0; i<4*16; i++){\
		int_revert (integer(state, i));\
	}\
} while(0);


HashReturn Compress(hashState *state)
{
	/*	Saving the old chaining variable for feedforwarding			*/
	unsigned int oldcv[2*16];
	unsigned int i;
	unsigned long long c0, c1;

 	/*	Loading the message in the state					*/
	LOADmessage(state, state->data);

	/*	Preparing the Feedforward operation					*/
	SAVEcv(state, oldcv);

	r = state->r;
	state->CNT += (1-state->padding) * state->m_blocksize + state->padding * state->messbitlen;

	CNT_r = state->CNT;

	if(endian == ET_BIG_ENDIAN){
		reverse_state(state->state);
	}

	do {
	/*	Applying 2 rounds AES on each word					*/

		BIG_SUB_WORDS(state);

	/*	Shift rows								*/

		BIG_SHIFT_ROWS(state);

	/*	Mix columns								*/
		BIG_MIX_COLUMN(state);

		r--;
	} while(r);

	if(endian == ET_BIG_ENDIAN){
		reverse_state(state->state);
	}

	/*	Feedforwarding 								*/
	FEEDforward(state, oldcv);

	return SUCCESS;
}

HashReturn Update(hashState *state, const BitSequence *data, DataLength databitlen){

	unsigned int i;
	unsigned long long n_blocks;

	if(databitlen == 0){
		return SUCCESS;
	}
	/*	Last call twice error ?							*/
	if(state->messbitlen % 8 != 0){
		return UPDATE_WBITS_TWICE;
	}
	/*	Take the input bits and concatenate to the existing data				*/
	n_blocks = (databitlen + state->messbitlen)/state->m_blocksize;

	/*	Process if enough data									*/
	for(i=0; i<n_blocks; i++){
		if(i == 0)
			memcpy(state->data + (state->messbitlen >> 3), data, (state->m_blocksize-state->messbitlen) >> 3);
		else
			memcpy(state->data, data + (i-1)*(state->m_blocksize >> 3) + ((state->m_blocksize-state->messbitlen) >> 3), state->m_blocksize >> 3);
	/*	Update the internal state and feedforward						*/
		Compress(state);
	}

	/*	Store the remaining bits for the next update						*/
	if(((databitlen + state->messbitlen) % state->m_blocksize) != 0){
		if(n_blocks >= 1){
			unsigned int remaining_bits = ((databitlen + state->messbitlen) % state->m_blocksize);
			memcpy(state->data, data + (n_blocks-1)*(state->m_blocksize >> 3) + ((state->m_blocksize-state->messbitlen) >> 3), remaining_bits >> 3);
			state->messbitlen = remaining_bits;
			/*	Treat the last bits							*/
			if(databitlen % 8 != 0)
				state->data[(state->messbitlen >> 3)] = flip_bits(flip_bits(data[(databitlen >> 3)]) & (0xff >> (8-(databitlen%8))));
		}
	/*	If there was not enough data, concatenate and wait for the next update			*/
	/*	or final										*/
		else{
			memcpy(state->data + (state->messbitlen >> 3), data, databitlen >> 3);
			state->messbitlen += databitlen;
			/*	Treat the last bits							*/
			if(databitlen % 8 != 0)
				state->data[(state->messbitlen >> 3)] = flip_bits(flip_bits(data[(databitlen >> 3)]) & (0xff >> (8-(databitlen%8))));
		}
	}
	/*	Case where we processed all the data in the buffer					*/
	else
		state->messbitlen = 0;

	return SUCCESS;
}

/***************************Finialisation routines***************************************/
HashReturn Final(hashState *state, BitSequence *hashval)
{
  	unsigned int i;
	unsigned short int hashlen = 0;
	unsigned long long messlen_l = 0;
	unsigned long long messlen_h = 0;

  	/* First, we initialize the hash value										*/
	for(i=0; i<(state->hashbitlen >> 3); i++)
  		hashval[i] = 0;
  	/* Then, we pad the message											*/
  	if(state->messbitlen <= (state->m_blocksize-145)){
  	/*	We can perform a one block padding									*/
 	/*	We add a 1 bit 												*/
		unsigned char c = flip_bits(state->data[state->messbitlen >> 3]);
	/*	Enough room for two bits in the last byte ?								*/
		if((state->messbitlen%8) != 0){
			c &= ~(0xff << (state->messbitlen%8));
			state->data[state->messbitlen >> 3] = flip_bits(c ^ (0x1 << (state->messbitlen % 8)));
		}
	/*	We pad the remaining bits										*/
		memset(state->data + (state->messbitlen >> 3) + 1, 0, (state->m_blocksize >> 3)- (144 >> 3) - (state->messbitlen >> 3) - 1 );
		if((state->messbitlen%8) == 0)
			state->data[(state->messbitlen >> 3)] = 0x80;
	/*	Prepare the message length and the hash length								*/
		PushString(state->hashbitlen, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3)), USINT);
		PushString(state->CNT + state->messbitlen, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3) + 2), ULL);
		PushString(0, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3) + 10), ULL);

	/*	We finalize the compression on the last block								*/
		if((state->messbitlen%state->m_blocksize) == 0){
			state->messbitlen = 0;
			state->CNT = 0;
		}
		state->padding = 1;
		Compress(state);
  	 }
  	else{
		unsigned char c = flip_bits(state->data[state->messbitlen >> 3]);
  	/*	Not enough room for one block : two blocks padding							*/
		if((state->messbitlen%8) != 0){
			c &= ~(0xff << (state->messbitlen%8));
			state->data[state->messbitlen >> 3] = flip_bits(c ^ (0x1 << (state->messbitlen % 8)));
		}
		memset(state->data + (state->messbitlen >> 3) + 1, 0, (state->m_blocksize >> 3) - (state->messbitlen >> 3) - 1);
		if((state->messbitlen%8) == 0)
			state->data[(state->messbitlen >> 3)] = 0x80;

	/*	We finalize the compression on the last blocks (the first one)						*/
		state->padding = 1;
		Compress(state);

	/*	Prepare the message length and the hash length								*/
		memset(state->data, 0, (state->m_blocksize >> 3));
		PushString(state->hashbitlen, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3)), USINT);
		PushString(state->CNT, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3) + 2), ULL);
		PushString(0, (unsigned char*)(state->data + (state->m_blocksize >> 3) - (144 >> 3) + 10), ULL);

	/*	We finalize the compression on the last blocks (the second one)						*/
		state->padding = 1;
		state->messbitlen = 0;
		state->CNT = 0;
		Compress(state);
	}
	/*	We truncate the chaining variable to get the hash value							*/
	if(state->hashbitlen%8 == 0){
		memcpy(hashval, state->state, (state->hashbitlen)/8);
	}
	else{
		((unsigned char*)state->state)[((state->hashbitlen)/8)] = \
		((unsigned char*)state->state)[((state->hashbitlen)/8)] & (0xff << (8-(state->hashbitlen%8)));
		memcpy(hashval, state->state, ((state->hashbitlen)/8) + 1);
	}

  	return SUCCESS;
}



HashReturn Hash(int hashbitlen, const BitSequence *data, DataLength databitlen, BitSequence *hashval)
{
  HashReturn S;
  hashState state;
  S = Init(&state, hashbitlen);
  if(S != SUCCESS) return S;
  S = Update(&state, data, databitlen);
  if(S != SUCCESS) return S;
  return Final(&state, hashval);
}





////////////////////////////////////////////////////////////////////////////////////
typedef enum { KAT_SUCCESS = 0, KAT_FILE_OPEN_ERROR = 1, KAT_HEADER_ERROR = 2, KAT_DATA_ERROR = 3, KAT_HASH_ERROR = 4 } STATUS_CODES;

STATUS_CODES    genShortMsg(int hashbitlen);
STATUS_CODES    genLongMsg(int hashbitlen);
STATUS_CODES    genExtremelyLongMsg(int hashbitlen);
STATUS_CODES    genMonteCarlo(int hashbitlen);
int             FindMarker(FILE *infile, const char *marker);
int             ReadHex(FILE *infile, BitSequence *A, int Length, char *str);
void    fprintBstr(FILE *fp, char *S, BitSequence *A, int L);

STATUS_CODES
main()
{
        int             i, ret_val,  bitlens[4] = { 224, 256, 384, 512 };
        unsigned long timeStart = clock();
        for ( i=0; i<4; i++ ) {
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genShortMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genExtremelyLongMsg(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
                printf("%lums:\n\t", clock() - timeStart);
                if ( (ret_val = genMonteCarlo(bitlens[i])) != KAT_SUCCESS )
                        return ret_val;
        }
        printf("overall test time: %lums", clock() - timeStart);
        return KAT_SUCCESS;
}

STATUS_CODES
genShortMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[256], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genShortMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ShortMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ShortMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genShortMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else {
                        done = 1;
                        break;
                }
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <ShortMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished ShortMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        int                     msglen, msgbytelen, done;
        BitSequence     Msg[4288], MD[64];
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("LongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <LongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        done = 0;
        timeTest(0, logFile);   //test start
        do {
                if ( FindMarker(fp_in, "Len = ") )
                        fscanf(fp_in, "%d", &msglen);
                else
                        break;
                msgbytelen = (msglen+7)/8;

                if ( !ReadHex(fp_in, Msg, msgbytelen, "Msg = ") ) {
                        printf("ERROR: unable to read 'Msg' from <LongMsgKAT.txt>\n");
                        return KAT_DATA_ERROR;
                }
                timeTest(1, logFile);   //unit test start
                Hash(hashbitlen, Msg, msglen, MD);
                timeTest(2, logFile);   //unit test stop
        } while ( !done );
        timeTest(3, logFile);   //test stop
        fclose(logFile);
        printf("\tfinished LongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genExtremelyLongMsg(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Text[65], MD[64];
        int                     i, repeat;
        FILE            *fp_in;
        hashState       state;
        HashReturn      retval;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genExtremelyLongMsg_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("ExtremelyLongMsgKAT.txt", "r")) == NULL ) {
                printf("Couldn't open <ExtremelyLongMsgKAT.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genExtremelyLongMsg: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }


        if ( FindMarker(fp_in, "Repeat = ") )
                fscanf(fp_in, "%d", &repeat);
        else {
                printf("ERROR: unable to read 'Repeat' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

        if ( FindMarker(fp_in, "Text = ") )
                fscanf(fp_in, "%s", Text);
        else {
                printf("ERROR: unable to read 'Text' from <ExtremelyLongMsgKAT.txt>\n");
                return KAT_DATA_ERROR;
        }

//      memcpy(Text, "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno", 64);
        timeTest(0, logFile);   //test start
        timeTest(1, logFile);   //unit test start
        if ( (retval = Init(&state, hashbitlen)) != KAT_SUCCESS ) {
                printf("Init returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        for ( i=0; i<repeat; i++ )
        {
                if (i % 8192 == 0) timeTest(1, logFile);        //unit test start
                if ( (retval = Update(&state, Text, 512)) != KAT_SUCCESS ) {
                        printf("Update returned <%d> in genExtremelyLongMsg\n", retval);
                        return KAT_HASH_ERROR;
                }
                if (i % 8192 == 0) timeTest(2, logFile);        //unit test stop
        }
        timeTest(1, logFile);   //unit test start
        if ( (retval = Final(&state, MD)) != KAT_SUCCESS ) {
                printf("Final returned <%d> in genExtremelyLongMsg\n", retval);
                return KAT_HASH_ERROR;
        }
        timeTest(2, logFile);   //unit test stop
        timeTest(3, logFile);   //test stop
        printf("\tfinished ExtremelyLongMsgKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

STATUS_CODES
genMonteCarlo(int hashbitlen)
{
        char            line[SUBMITTER_INFO_LEN];
        BitSequence     Seed[128], Msg[128], MD[64], Temp[128];
        int                     i, j, bytelen;
        FILE            *fp_in;
        char            logFileName [100];
        FILE            *logFile;

        sprintf(logFileName, "%s_genMonteCarlo_%d.log", ALGORITHM_NAME, hashbitlen);
        printf("logging results to: %s\n", logFileName);
        logFile = fopen(logFileName, "w");

        if ( (fp_in = fopen("MonteCarlo.txt", "r")) == NULL ) {
                printf("Couldn't open <MonteCarlo.txt> for read\n");
                return KAT_FILE_OPEN_ERROR;
        }

        if ( FindMarker(fp_in, "# Algorithm Name:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Algorithm Name\n");
                return KAT_HEADER_ERROR;
        }
        if ( FindMarker(fp_in, "# Principal Submitter:") ) {
                fscanf(fp_in, "%[^\n]\n", line);
        }
        else {
                printf("genMonteCarlo: Couldn't read Principal Submitter\n");
                return KAT_HEADER_ERROR;
        }

        if ( !ReadHex(fp_in, Seed, 128, "Seed = ") ) {
                printf("ERROR: unable to read 'Seed' from <MonteCarlo.txt>\n");
                return KAT_DATA_ERROR;
        }

        bytelen = hashbitlen / 8;
        memcpy(Msg, Seed, 128);
        timeTest(0, logFile);   //test start
        for ( j=0; j<100; j++ ) {
                for ( i=0; i<1000; i++ ) {
                        timeTest(1, logFile);   //unit test start
                        Hash(hashbitlen, Msg, 1024, MD);
                        timeTest(2, logFile);   //unit test stop
                        memcpy(Temp, Msg, 128-bytelen);
                        memcpy(Msg, MD, bytelen);
                        memcpy(Msg+bytelen, Temp, 128-bytelen);
                }
        }
        timeTest(3, logFile);   //test stop
        printf("\tfinished MonteCarloKAT for <%d>\n", hashbitlen);

        fclose(fp_in);

        return KAT_SUCCESS;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
FindMarker(FILE *infile, const char *marker)
{
        char    line[MAX_MARKER_LEN];
        int             i, len;

        len = (int)strlen(marker);
        if ( len > MAX_MARKER_LEN-1 )
                len = MAX_MARKER_LEN-1;

        for ( i=0; i<len; i++ )
                if ( (line[i] = fgetc(infile)) == EOF )
                        return 0;
        line[len] = '\0';

        while ( 1 ) {
                if ( !strncmp(line, marker, len) )
                        return 1;

                for ( i=0; i<len-1; i++ )
                        line[i] = line[i+1];
                if ( (line[len-1] = fgetc(infile)) == EOF )
                        return 0;
                line[len] = '\0';
        }

        // shouldn't get here
        return 0;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
ReadHex(FILE *infile, BitSequence *A, int Length, char *str)
{
        int                     i, ch, started;
        BitSequence     ich;

        if ( Length == 0 ) {
                A[0] = 0x00;
                return 1;
        }
        memset(A, 0x00, Length);
        started = 0;
        if ( FindMarker(infile, str) )
                while ( (ch = fgetc(infile)) != EOF ) {
                        if ( !isxdigit(ch) ) {
                                if ( !started ) {
                                        if ( ch == '\n' )
                                                break;
                                        else
                                                continue;
                                }
                                else
                                        break;
                        }
                        started = 1;
                        if ( (ch >= '0') && (ch <= '9') )
                                ich = ch - '0';
                        else if ( (ch >= 'A') && (ch <= 'F') )
                                ich = ch - 'A' + 10;
                        else if ( (ch >= 'a') && (ch <= 'f') )
                                ich = ch - 'a' + 10;

                        for ( i=0; i<Length-1; i++ )
                                A[i] = (A[i] << 4) | (A[i+1] >> 4);
                        A[Length-1] = (A[Length-1] << 4) | ich;
                }
        else
                return 0;

        return 1;
}

void
fprintBstr(FILE *fp, char *S, BitSequence *A, int L)
{
        int             i;

        fprintf(fp, "%s", S);

        for ( i=0; i<L; i++ )
                fprintf(fp, "%02X", A[i]);

        if ( L == 0 )
                fprintf(fp, "00");

        fprintf(fp, "\n");
}
